﻿// Copyright (c) 2019 .NET Foundation and Contributors. All rights reserved.
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for full license information.

using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Reactive;
using System.Reactive.Linq;

namespace ReactiveUI.Winforms.Legacy
{
    /// <summary>
    /// A set of extension methods associated with the IReactiveDerivedBindingList{TNew}.
    /// </summary>
    [Obsolete("ReactiveList is no longer supported. We suggest replacing it with DynamicData https://github.com/rolandpheasant/dynamicdata")]
    public static class ObservableCollectionMixin
    {
        /// <summary>
        /// Creates a collection whose contents will "follow" another
        /// collection; this method is useful for creating ViewModel collections
        /// that are automatically updated when the respective Model collection
        /// is updated.
        ///
        /// Note that even though this method attaches itself to any
        /// IEnumerable, it will only detect changes from objects implementing
        /// INotifyCollectionChanged (like ReactiveList). If your source
        /// collection doesn't implement this, signalReset is the way to signal
        /// the derived collection to reorder/refilter itself.
        /// </summary>
        /// <typeparam name="T">The type.</typeparam>
        /// <typeparam name="TNew">The new type.</typeparam>
        /// <typeparam name="TDontCare">The signal type.</typeparam>
        /// <param name="collection">The collection we are creating a derived list from.</param>
        /// <param name="selector">A Select function that will be run on each
        /// item.</param>
        /// <param name="removed">An action that is called on each item when
        /// it is removed.</param>
        /// <param name="filter">A filter to determine whether to exclude items
        /// in the derived collection.</param>
        /// <param name="orderer">A comparator method to determine the ordering of
        /// the resulting collection.</param>
        /// <param name="signalReset">When this Observable is signalled,
        /// the derived collection will be manually
        /// reordered/refiltered.</param>
        /// <returns>A new collection whose items are equivalent to
        /// Collection.Select().Where().OrderBy() and will mirror changes
        /// in the initial collection.</returns>
        public static IReactiveDerivedBindingList<TNew> CreateDerivedBindingList<T, TNew, TDontCare>(
            this IEnumerable<T> collection,
            Func<T, TNew> selector,
            Action<TNew> removed,
            Func<T, bool> filter = null,
            Func<TNew, TNew, int> orderer = null,
            IObservable<TDontCare> signalReset = null)
        {
            Contract.Requires(selector != null);

            IObservable<Unit> reset = null;

            if (signalReset != null)
            {
                reset = signalReset.Select(_ => Unit.Default);
            }

            return new ReactiveDerivedBindingList<T, TNew>(collection, selector, filter, orderer, removed, reset);
        }

        /// <summary>
        /// Creates a collection whose contents will "follow" another
        /// collection; this method is useful for creating ViewModel collections
        /// that are automatically updated when the respective Model collection
        /// is updated.
        ///
        /// Note that even though this method attaches itself to any
        /// IEnumerable, it will only detect changes from objects implementing
        /// INotifyCollectionChanged (like ReactiveList). If your source
        /// collection doesn't implement this, signalReset is the way to signal
        /// the derived collection to reorder/refilter itself.
        /// </summary>
        /// <typeparam name="T">The type.</typeparam>
        /// <typeparam name="TNew">The new type.</typeparam>
        /// <typeparam name="TDontCare">The signal type.</typeparam>
        /// <param name="collection">The collection we are creating a derived list from.</param>
        /// <param name="selector">A Select function that will be run on each
        /// item.</param>
        /// <param name="filter">A filter to determine whether to exclude items
        /// in the derived collection.</param>
        /// <param name="orderer">A comparator method to determine the ordering of
        /// the resulting collection.</param>
        /// <param name="signalReset">When this Observable is signalled,
        /// the derived collection will be manually
        /// reordered/refiltered.</param>
        /// <returns>A new collection whose items are equivalent to
        /// Collection.Select().Where().OrderBy() and will mirror changes
        /// in the initial collection.</returns>
        public static IReactiveDerivedBindingList<TNew> CreateDerivedBindingList<T, TNew, TDontCare>(
            this IEnumerable<T> collection,
            Func<T, TNew> selector,
            Func<T, bool> filter = null,
            Func<TNew, TNew, int> orderer = null,
            IObservable<TDontCare> signalReset = null)
        {
            return collection.CreateDerivedBindingList(selector, null, filter, orderer, signalReset);
        }

        /// <summary>
        /// Creates a collection whose contents will "follow" another
        /// collection; this method is useful for creating ViewModel collections
        /// that are automatically updated when the respective Model collection
        /// is updated.
        ///
        /// Be aware that this overload will result in a collection that *only*
        /// updates if the source implements INotifyCollectionChanged. If your
        /// list changes but isn't a ReactiveList/ObservableCollection,
        /// you probably want to use the other overload.
        /// </summary>
        /// <typeparam name="T">The type.</typeparam>
        /// <typeparam name="TNew">The new type.</typeparam>
        /// <param name="collection">The collection we are creating a derived list from.</param>
        /// <param name="selector">A Select function that will be run on each
        /// item.</param>
        /// <param name="removed">An action that is called on each item when
        /// it is removed.</param>
        /// <param name="filter">A filter to determine whether to exclude items
        /// in the derived collection.</param>
        /// <param name="orderer">A comparator method to determine the ordering of
        /// the resulting collection.</param>
        /// <returns>A new collection whose items are equivalent to
        /// Collection.Select().Where().OrderBy() and will mirror changes
        /// in the initial collection.</returns>
        public static IReactiveDerivedBindingList<TNew> CreateDerivedBindingList<T, TNew>(
            this IEnumerable<T> collection,
            Func<T, TNew> selector,
            Action<TNew> removed,
            Func<T, bool> filter = null,
            Func<TNew, TNew, int> orderer = null)
        {
            return collection.CreateDerivedBindingList(selector, removed, filter, orderer, (IObservable<Unit>)null);
        }

        /// <summary>
        /// Creates a collection whose contents will "follow" another
        /// collection; this method is useful for creating ViewModel collections
        /// that are automatically updated when the respective Model collection
        /// is updated.
        ///
        /// Be aware that this overload will result in a collection that *only*
        /// updates if the source implements INotifyCollectionChanged. If your
        /// list changes but isn't a ReactiveList/ObservableCollection,
        /// you probably want to use the other overload.
        /// </summary>
        /// <typeparam name="T">The type.</typeparam>
        /// <typeparam name="TNew">The new type.</typeparam>
        /// <param name="collection">The collection we are creating a derived list from.</param>
        /// <param name="selector">A Select function that will be run on each
        /// item.</param>
        /// <param name="filter">A filter to determine whether to exclude items
        /// in the derived collection.</param>
        /// <param name="orderer">A comparator method to determine the ordering of
        /// the resulting collection.</param>
        /// <returns>A new collection whose items are equivalent to
        /// Collection.Select().Where().OrderBy() and will mirror changes
        /// in the initial collection.</returns>
        public static IReactiveDerivedBindingList<TNew> CreateDerivedBindingList<T, TNew>(
            this IEnumerable<T> collection,
            Func<T, TNew> selector,
            Func<T, bool> filter = null,
            Func<TNew, TNew, int> orderer = null)
        {
            return collection.CreateDerivedBindingList(selector, null, filter, orderer, (IObservable<Unit>)null);
        }
    }
}
